/**
 * Created with IntelliJ IDEA.
 * User: Aleksandr Sereda
 * Date: 07.06.13
 * Time: 19:45
 */
package com.google.code.bankagregator.adapter.bank.brokbiz.reader.credit;

import com.google.code.bankagregator.adapter.exception.AdapterException;
import com.google.code.bankagregator.adapter.exception.AdapterRecognizeDataException;
import com.google.code.bankagregator.common.DetectCurrency;
import com.google.code.bankagregator.common.SiteConnect;
import com.google.code.bankagregator.common.reader.CreditReader;
import com.google.code.bankagregator.entities.*;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.util.ArrayList;
import java.util.List;

public class CreditBBBNeotlozhni implements CreditReader {

    private final String PRODUCT_URL = "http://www.bankbb.com.ua/print/bank_for_family/credits_fam/auto.html";
    private final String HTML_PARSE_FILTER = "table tbody tr td table tbody tr td";
    private final String PRODUCT_NAME = "На неотложные нужды";
    private final int MONTHS_PER_YEAR = 12;
    private final int DAYS_PER_MONTH = 31;
    private final String MSG_PARSE_FAIL = "BBB markup changed. Parsing fail.";

    @Override
    public List<Credit> getCredits() throws AdapterException {
        List<Credit> credits = new ArrayList<>();
        Credit credit = new Credit();
        Document page = new SiteConnect(PRODUCT_URL).getPage();

        Elements elements = page.select(HTML_PARSE_FILTER);

        credit.setName(PRODUCT_NAME);
        credit.setType(CreditType.CONSUMER);
        credit.setPrepayment(0.0);
        credit.setUrl(PRODUCT_URL);
        credit.setState(State.ACTIVE);

        Bank bank = new Bank();
        bank.setName("АО \"БРОКБИЗНЕСБАНК\"");
        bank.setUrl("http://www.bankbb.com.ua/");

        credit.setBank(bank);

        int term = getTermFromText(elements);
        credit.setTerm(term);

        double rate = getRateFromText(elements);
        credit.setCreditRate(rate);

        String minAmountString = getMinAmountDataFromText(elements);
        credit.setMinAmount(searchMinAmount(minAmountString));

        DetectCurrency detect = new DetectCurrency();
        credit.setCurrency(Currency.valueOf(detect.getCurrencyAbbreviation(minAmountString)));

        int maxAmount = getMaxAmountFromText(elements);
        credit.setMaxAmount(maxAmount);

        credits.add(credit);
        return credits;
    }

    /**
     * Search for min amount and currency data in text.
     *
     * @param elements
     * @return
     * @throws AdapterRecognizeDataException
     */
    public String getMinAmountDataFromText(Elements elements) throws AdapterRecognizeDataException {
        for (Element elm : elements) {
            if (searchMinAmount(elm.text()) > 0) {
                return elm.text();
            }
        }
        throw new AdapterRecognizeDataException(MSG_PARSE_FAIL);
    }

    /**
     * Search for rate data in text.
     *
     * @param elements
     * @return
     * @throws AdapterRecognizeDataException
     */
    public double getRateFromText(Elements elements) throws AdapterRecognizeDataException {
        for (Element elm : elements) {
            double rate = searchRate(elm.text());
            if (rate > 0) {
                return rate;
            }
        }
        throw new AdapterRecognizeDataException(MSG_PARSE_FAIL);
    }

    /**
     * Search for term data in text
     *
     * @param elements
     * @return
     * @throws AdapterRecognizeDataException
     */
    public int getTermFromText(Elements elements) throws AdapterRecognizeDataException {
        for (Element elm : elements) {
            int term = searchTerm(elm.text());
            if (term > 0) {
                return term;
            }
        }
        throw new AdapterRecognizeDataException(MSG_PARSE_FAIL);
    }

    /**
     * Search for max amount data in text
     *
     * @param elements
     * @return
     * @throws AdapterRecognizeDataException
     */
    public int getMaxAmountFromText(Elements elements) throws AdapterRecognizeDataException {
        for (Element elm : elements) {
            int max = searchMaxAmount(elm.text());
            if (max > 0) {
                return max;
            }
        }
        throw new AdapterRecognizeDataException(MSG_PARSE_FAIL);
    }

    /**
     * Search for string with product term data. Primitive algorithm.
     *
     * @param string contains data to analyse
     * @return days of product term or -1 if data unrecognized.
     */
    public int searchTerm(String string) {
        if (string.replaceAll("[^0-9]+", "").isEmpty()) {
            return -1;
        }
        String[] keywords = {"дней", "день", "месяц", "месяцев", "год", "лет"};
        String timeSave = "";
        int numSave = -1;
        for (String key : keywords) {
            if (string.toLowerCase().contains(key)) {
                numSave = Integer.valueOf(string.replaceAll("[^0-9]+", ""));
                timeSave = key;
                switch (timeSave) {
                    case "дней":
                        return numSave;
                    case "день":
                        return numSave;
                    case "месяц":
                        return DAYS_PER_MONTH * numSave;
                    case "месяцев":
                        return DAYS_PER_MONTH * numSave;
                    case "год":
                        return MONTHS_PER_YEAR * DAYS_PER_MONTH * numSave;
                    case "лет":
                        return MONTHS_PER_YEAR * DAYS_PER_MONTH * numSave;
                }
            }
        }
        return -1;
    }

    /**
     * Search for rate. Primitive algorithm.
     *
     * @param string contains data to analyse
     * @return percents of product rate or -1.0 if data unrecognized.
     */
    public double searchRate(String string) {
        String num = string.replaceAll("[^0-9.,]+", "");
        num = num.replaceAll(",", ".");
        if (string.contains("%") && (num.length() > 0)) {
            return Double.valueOf(num);
        }
        return -1.0;
    }

    /**
     * Search for numbers in text string and get min value.
     *
     * @param string specified text.
     * @return Min value (positive integer) or -1 if text line is not in appropriate format.
     */
    public int searchMinAmount(String string) {
        String[] s = getNumbers(string);
        if (s.length == 2) {
            return Math.min(Integer.valueOf(s[0]), Integer.valueOf(s[1]));
        }
        return -1;
    }

    /**
     * Search for numbers in text string and get max value.
     *
     * @param string specified text.
     * @return Max value (positive integer) or -1 if text line is not in appropriate format.
     */
    public int searchMaxAmount(String string) {
        String[] s = getNumbers(string);
        if (s.length == 2) {
            return Math.max(Integer.valueOf(s[0]), Integer.valueOf(s[1]));
        }
        return -1;
    }

    /**
     * Get numbers from text line. Remove spaces.
     *
     * @param string specified text line.
     * @return String[] with numbers.
     */
    public String[] getNumbers(String string) {
        String glueNums = string.replaceAll("\\s000", "000");
        String onlyNums = glueNums.replaceAll("[^0-9]+", " ");
        String clearSpace = onlyNums.replaceAll("~[ ]+~u", " ").trim();
        return clearSpace.split(" ");
    }
}
